#include <stdio.h>
#include <kewinternal.h>

#define SOURCE_FILE_NAME "Exception"

struct KewExceptionHandlerFrame *kew_top_exception_handler_frame;


METHOD(Raise)
{
  KewObject *result;
  struct KewExceptionHandlerFrame *top_frame = kew_handler_frame(receiver);

  if (top_frame == NULL)
    result = KewSend1(kew_default_handler(receiver), "do", args[0]);
  else {
    kew_handler_frame(receiver) = kew_handler_frame(receiver)->next;

    result = KewSend1(top_frame->block, "do", args[0]);

    kew_handler_frame(receiver) = top_frame;
  }
  results[0] = result;
}


KewObjectType kew_exception_type = {
  SOURCE_FILE_NAME, __LINE__,
  2,
  {
    {"hash", Kew__GenericHash, 0, 1},
    {"=", Kew__GenericEqual, 1, 1},
    {"raise", Raise, 1, 1},
    {NULL, NULL, 0, 0}
  }
};



/* The factory object */

METHOD(FactoryNew)
{
  KewObject *result = KewNewObject(&kew_exception_type);
  kew_handler_frame(result) = NULL;
  kew_default_handler(result) = args[0];
  results[0] = result;
}

static KewObjectType exception_factory_type = {
  SOURCE_FILE_NAME, __LINE__,
  0,
  {
    {"hash", Kew__GenericHash, 0, 0},
    {"=", Kew__GenericEqual, 1, 1},
    {"new", FactoryNew, 1, 1},
    {NULL, NULL, 0, 0}
  }
};


/* FIXME: this should go to the container */
METHOD(DefaultExceptionHandler)
{
  /* FIXME: export types so that we can check args[0] is a string here */
  fprintf(stderr, "\n*** EXCEPTION ***\n%s\n", KewStringVal(args[0]));
  KewPrintStackTrace();
  exit(1);
}


void
Kew__InitException(void)
{
  KewException = KewNewObject(&exception_factory_type);
  kew_top_exception_handler_frame = NULL;

  KewDefaultExceptionHandler = KewNewBlock(DefaultExceptionHandler,
                                           1, 1, 1);
  KewMissingMethod = KewSend1(KewException, "new",
                              KewDefaultExceptionHandler);
  KewWrongObjectType = KewSend1(KewException, "new",
                                KewDefaultExceptionHandler);
  KewContinuationAlreadyTaken = KewSend1(KewException, "new",
                                         KewDefaultExceptionHandler);
  KewMissingKey = KewSend1(KewException, "new",
                           KewDefaultExceptionHandler);
}
